package wiiusej.motionAlgorithms;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;
import java.util.Vector;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import wiiusej.Wiimote;
import wiiusej.apptest.WiimoteKmeansListener;
import wiiusej.wiiusejevents.utils.WiimoteBasicListener;


public class CalibrationController implements Runnable {

	//Logger	
	private static Logger fileLogger;
	
	//WIIMOTE DATA_STRUCTURES
	private static HashMap<Integer, Wiimote> wiimotes;
	private static HashMap<Integer, WiimoteBasicListener> wiimoteListeners;
	
	//CENTROIDS - defined by heuristics by taking motion samples
	private CentroidsEnum centroidsType;
	private Vector<Centroid> centroids;
	
	//COM QUEUES
	private static BlockingQueue<CentroidsEnum> calibrationKmeans = new LinkedBlockingDeque<CentroidsEnum>();
	private Vector<CentroidsEnum> listenersAnswers = new Vector<CentroidsEnum>();
	
	//STATE MACHINE
	private CentroidsEnum stateM;	
	private final int STOP = 0;
	private final int START = 1;
	private final int GAME = 2; 
	private boolean play = false;
	
	//OTHERS
	private final long SLEEP_TIME = 5000; //5 seconds
	
	public CalibrationController(HashMap<Integer, Wiimote> wiimotes, HashMap<Integer, 
			WiimoteBasicListener> wiimoteListeners, BlockingQueue<CentroidsEnum> calibrationKmeans){
		
		this.wiimotes = (HashMap<Integer, Wiimote>) wiimotes.clone();
		this.wiimoteListeners = wiimoteListeners;
		this.calibrationKmeans = calibrationKmeans;
		
		//Logger init
		PropertyConfigurator.configure("log4j.properties");
		fileLogger = Logger.getLogger("calibrationController");
		
		fileLogger.debug("WiimoteListeners: " + wiimoteListeners);
		
	}
	
	@Override
	public void run() {
		
		createCentroidsStructure();
		
		while(true){
			
			while(!play){
			
				String userInput = new String();
				fileLogger.info("Start APP [Y/N]?");
				Scanner in = new Scanner(System.in);
				userInput = in.nextLine();
				
				fileLogger.info("Introduced Command: " + userInput);
				if(userInput.equals("Y") || userInput.equals("y")){
					
					fileLogger.info("");
					
					//SLOW MOTION CALIBRATION
					fileLogger.info("SLOW motion Calibration....Start!");
					startStopListeners(CentroidsEnum.SLOW, START);
					//Wait for all the answer
					while(!waitForAnswers(calibrationKmeans.poll())){						
					}
					
					//BIG MOTION CALIBRATION
					fileLogger.info("BIG motion Calibration....Start!");
					sleep();
					startStopListeners(CentroidsEnum.BIG, START);
					//Wait for all the answer
					while(!waitForAnswers(calibrationKmeans.poll())){						
					}
					
					//ROBOTIC MOTION CALIBRATION
					fileLogger.info("ROBOTIC motion Calibration....Start!");
					sleep();
					startStopListeners(CentroidsEnum.ROBOTIC, START);
					//Wait for all the answer
					while(!waitForAnswers(calibrationKmeans.poll())){						
					}
								
					//CALIBRATION END					
					fileLogger.info("CALIBRATION FINISHED!!");
					
					//PRINT RESULTS
					
					userInput = null;
					
					fileLogger.info("Init Game? [Y/N]");					
					Scanner in2 = new Scanner(System.in);
					userInput = in2.nextLine();
					
					if(userInput.equals("N") || userInput.equals("n")){
						break;
					}else if(userInput.equals("Y") || userInput.equals("y")){
						fileLogger.info("STARTING THE GAME!!");						
						sleep();						
						startStopListeners(null, GAME);		
						play = true;
						
						
					}
					
					
				}else if(userInput=="N" || userInput=="n"){
					fileLogger.info("Finishing APP!!");
					break;
				}else{
					
					fileLogger.info("Non-valid Command!");
				}
				
			}
			
			
		}
		
				
	}

	private void sleep() {
		try {
			Thread.sleep(SLEEP_TIME);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
	}

	private boolean waitForAnswers(CentroidsEnum centroidsEnum){
		
		if(centroidsEnum!=null){
			listenersAnswers.add(centroidsEnum);
		}
		
		if(listenersAnswers.size() == wiimoteListeners.size()){
			listenersAnswers.clear();
			return true;			
		}		
		
		return false;
	}
	private void startStopListeners(CentroidsEnum type, int action) {
		Iterator it = wiimoteListeners.entrySet().iterator();		
		
		//upload current state machine
		stateM = type;	
			
		while(it.hasNext()){
			Map.Entry entry = (Map.Entry) it.next();			
			WiimoteKmeansListener aux = (WiimoteKmeansListener) entry.getValue();
			
			switch(action){
			case START:
				aux.startListener(type);
				long id = aux.getWiimote().getId();
				fileLogger.debug("Starting Listener: " + id + " State: " + aux.getSM());
				break;
				
			case GAME:
				aux.startGame();
				break;
				
			case STOP: 
				aux.stopListener();
				break;
			default:				
				fileLogger.error("Action not defined!!");
			
			}
									
		}		
	}

	private void initCentroids() {		
		Centroid slowCentroid = new Centroid(centroidsType.SLOW);
		Centroid bigCentroid = new Centroid(centroidsType.BIG);
		Centroid roboticCentroid = new Centroid(centroidsType.ROBOTIC);
		
		centroids.add(slowCentroid);
		centroids.add(bigCentroid);
		centroids.add(roboticCentroid);
		
		fileLogger.debug("");
		fileLogger.debug("Adding Centroids:");
		for(Centroid c : centroids){
			fileLogger.debug(c.toString());			
		}
		
	}
	
	private void createCentroidsStructure() {
		
		
	}
	
	
	
	
	
	
	
	
	
	

}
